package com.zxing.slice;
/*
 * Copyright (C) 2021 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
import com.google.zxing.*;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.common.HybridBinarizer;
import com.google.zxing.qrcode.QRCodeWriter;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
import com.zxing.camera.BitmapLuminanceSource;
import com.zxing.decoding.DecodeFormatManager;
import com.zxing.utils.Log;
import com.zxing.utils.TextUtils;

import ohos.aafwk.ability.DataAbilityHelper;
import ohos.app.Context;
import ohos.media.image.ImageSource;
import ohos.media.image.PixelMap;
import ohos.media.image.common.PixelFormat;
import ohos.media.image.common.Position;
import ohos.media.image.common.Size;
import ohos.media.photokit.metadata.AVStorage;
import ohos.utils.net.Uri;

import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Hashtable;
import java.util.Vector;

/**
 * Created by aaron on 16/7/27.
 * 二维码扫描工具类
 */
public class CodeUtils {
    /**
     * 解析二维码图片工具类
     * @param context 上下文
     * @param path 路径
     * @param analyzeCallback 回调
     */
    public static void analyzeBitmap(Context context, String path, AnalyzeCallback analyzeCallback) {
        /**
         * 首先判断图片的大小,若图片过大,则执行图片的裁剪操作,防止OOM
         */
        Uri uri = Uri.appendEncodedPathToUri(AVStorage.Images.Media.EXTERNAL_DATA_ABILITY_URI, path);
        FileDescriptor file = null;
        FileInputStream in2 = null;
        try {
            file = DataAbilityHelper.creator(context).openFile(uri, "r");

            in2 = new FileInputStream(file);
            ImageSource source = ImageSource.create(in2, new ImageSource.SourceOptions());
            ImageSource.DecodingOptions options = new ImageSource.DecodingOptions();

            int sampleSize = (int) (source.getImageInfo().size.height / (float) 400);

            if (sampleSize <= 0) {
                sampleSize = 1;
            }
            options.sampleSize = sampleSize;
            PixelMap map = source.createPixelmap(new ImageSource.DecodingOptions());
            analyzeBitmap(map, analyzeCallback);
        } catch (Exception e) {
            if (analyzeCallback != null) {
                analyzeCallback.onAnalyzeFailed();
            }
        } finally {
            if (in2 != null) {
                try {
                    in2.close();
                } catch (IOException e) {
                    Log.error(e.getMessage());
                }
            }
        }
    }

    /**
     * 分析图片
     * @param map 图片
     * @param analyzeCallback 回调
     */
    public static void analyzeBitmap(PixelMap map, AnalyzeCallback analyzeCallback) {
        String result = parseInfoFromBitmap(map);
        if (result != null) {
            if (analyzeCallback != null) {
                analyzeCallback.onAnalyzeSuccess(map, result);
            }
        } else {
            if (analyzeCallback != null) {
                analyzeCallback.onAnalyzeFailed();
            }
        }
    }

    /**
     *  将map解析
     * @param map map
     * @return 字符串信息
     */
    public static String parseInfoFromBitmap(PixelMap map) {
        String result = null;
        MultiFormatReader multiFormatReader = new MultiFormatReader();

        // 解码的参数
        Hashtable<DecodeHintType, Object> hints = new Hashtable<DecodeHintType, Object>(3);
        // 可以解析的编码类型
        Vector<BarcodeFormat> decodeFormats = new Vector<BarcodeFormat>();
        if (decodeFormats.isEmpty()) {
            decodeFormats = new Vector<BarcodeFormat>();
            // 这里设置可扫描的类型，我这里选择了都支持
            decodeFormats.addAll(DecodeFormatManager.ONE_D_FORMATS);
            decodeFormats.addAll(DecodeFormatManager.QR_CODE_FORMATS);
            decodeFormats.addAll(DecodeFormatManager.DATA_MATRIX_FORMATS);
        }
        hints.put(DecodeHintType.POSSIBLE_FORMATS, decodeFormats);
        // 设置继续的字符编码格式为UTF8
        hints.put(DecodeHintType.CHARACTER_SET, "UTF8");
        // 设置解析配置参数
        multiFormatReader.setHints(hints);

        try {
            BinaryBitmap binaryBitmap = new BinaryBitmap(new HybridBinarizer(new BitmapLuminanceSource(map)));
            Result rawResult = multiFormatReader.decodeWithState(binaryBitmap);
            if (rawResult != null) {
                result = rawResult.getText();
            }
        } catch (Exception ignored) {
            Log.error(ignored.getMessage());
        }
        return result;
    }

    /**
     * 生成二维码图片
     * @param text 字符
     * @param wei 宽
     * @param hei 高
     * @param logo 图片
     * @return 图片信息
     */
    public static PixelMap createQRCode(String text, int wei, int hei, PixelMap logo) {
        if (TextUtils.isEmpty(text)) {
            return null;
        }
        try {
            PixelMap scaleLogo = getScaleLogo(logo, wei, hei);
            int offsetX = wei / 2;
            int offsetY = hei / 2;
            int scaleWidth = 0;
            int scaleHeight = 0;
            if (scaleLogo != null) {
                scaleWidth = scaleLogo.getImageInfo().size.width;
                scaleHeight = scaleLogo.getImageInfo().size.height;
                offsetX = (wei - scaleWidth) / 2;
                offsetY = (hei - scaleHeight) / 2;
            }
            Hashtable<EncodeHintType, Object> hints = new Hashtable<EncodeHintType, Object>();
            hints.put(EncodeHintType.CHARACTER_SET, "utf-8");
            hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);
            BitMatrix bitMatrix = new QRCodeWriter().encode(text, BarcodeFormat.QR_CODE, wei, hei, hints);
            int[] pixels = new int[wei * hei];
            for (int y = 0; y < hei; y++) {
                for (int x = 0; x < wei; x++) {
                    if (x >= offsetX && x < offsetX + scaleWidth && y >= offsetY && y < offsetY + scaleHeight) {
                        Position position = new Position(x - offsetX, y - offsetY);
                        int pixel = scaleLogo.readPixel(position);
                        if (pixel == 0) {
                            if (bitMatrix.get(x, y)) {
                                pixel = 0xff000000;
                            } else {
                                pixel = 0xffffffff;
                            }
                        }
                        pixels[y * wei + x] = pixel;
                    } else {
                        if (bitMatrix.get(x, y)) {
                            pixels[y * wei + x] = 0xff000000;
                        } else {
                            pixels[y * wei + x] = 0xffffffff;
                        }
                    }
                }
            }
            PixelMap.InitializationOptions options = new PixelMap.InitializationOptions();
            options.size = new Size(wei, hei);
            options.pixelFormat = PixelFormat.ARGB_8888;
            return PixelMap.create(pixels, options);
        } catch (Exception e) {
            Log.error(e.getMessage());
        }
        return null;
    }

    private static PixelMap getScaleLogo(PixelMap logo, int w, int h) {
        if (logo == null) {
            return null;
        }
        double scaleFactor =
                Math.min(w * 1.0f / 5 / logo.getImageInfo().size.width, h * 1.0f / 5 / logo.getImageInfo().size.height);
        PixelMap.InitializationOptions options = new PixelMap.InitializationOptions();
        Size size = new Size();
        size.width = (int) (logo.getImageInfo().size.width * scaleFactor);
        size.height = (int) (logo.getImageInfo().size.height * scaleFactor);
        options.size = size;
        return PixelMap.create(logo, options);
    }

    /**
     * 解析二维码结果
     */
    public interface AnalyzeCallback {
        /**
         * 分析成功
         * @param mBitmap 图片
         * @param result 结果
         */
        public void onAnalyzeSuccess(PixelMap mBitmap, String result);

        /**
         * 分析失败
         */
        public void onAnalyzeFailed();
    }
}
